/*
 * Copyright 2005 MH-Software-Entwicklung. All rights reserved.
 * Use is subject to license terms.
 */

package com.jtattoo.plaf;

import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.border.*;

/**
 * 
 * @author Michael Hagen
 */
public class BaseSpinnerUI extends BasicSpinnerUI {
	/**
	 * Used by the default LayoutManager class - SpinnerLayout for missing
	 * (null) editor/nextButton/previousButton children.
	 */
	private static final Dimension zeroSize = new Dimension(0, 0);

	private MyLayoutManager myLayoutManager = null;

	/**
	 * Returns a new instance of BaseSpinnerUI. SpinnerListUI delegates are
	 * allocated one per JSpinner.
	 * 
	 * @param c
	 *            the JSpinner (not used)
	 * @see ComponentUI#createUI
	 * @return a new BasicSpinnerUI object
	 */
	public static ComponentUI createUI(JComponent c) {
		return new BaseSpinnerUI();
	}

	/**
	 * Create a <code>LayoutManager</code> that manages the
	 * <code>editor</code>, <code>nextButton</code>, and
	 * <code>previousButton</code> children of the JSpinner. These three
	 * children must be added with a constraint that identifies their role:
	 * "Editor", "Next", and "Previous". The default layout manager can handle
	 * the absence of any of these children.
	 * 
	 * @return a LayoutManager for the editor, next button, and previous button.
	 * @see #createNextButton
	 * @see #createPreviousButton
	 * @see #createEditor
	 */
	protected LayoutManager createLayout() {
		if (myLayoutManager == null) {
			myLayoutManager = new MyLayoutManager();
		}
		return myLayoutManager;
	}

	protected Component createNextButton() {
		JButton button = new SpinButton(SwingConstants.NORTH);
		if (JTattooUtilities.isLeftToRight(spinner)) {
			Border border = BorderFactory.createMatteBorder(0, 1, 1, 0, AbstractLookAndFeel.getFrameColor());
			button.setBorder(border);
		} else {
			Border border = BorderFactory.createMatteBorder(0, 0, 1, 1, AbstractLookAndFeel.getFrameColor());
			button.setBorder(border);
		}
		installNextButtonListeners(button);
		return button;
	}

	protected Component createPreviousButton() {
		JButton button = new SpinButton(SwingConstants.SOUTH);
		if (JTattooUtilities.isLeftToRight(spinner)) {
			Border border = BorderFactory.createMatteBorder(0, 1, 0, 0, AbstractLookAndFeel.getFrameColor());
			button.setBorder(border);
		} else {
			Border border = BorderFactory.createMatteBorder(0, 0, 0, 1, AbstractLookAndFeel.getFrameColor());
			button.setBorder(border);
		}
		installPreviousButtonListeners(button);
		return button;
	}

	//------------------------------------------------------------------------------
	public static class SpinButton extends NoFocusButton {
		private static Dimension minSize = new Dimension(14, 12);
		private int direction = SwingConstants.NORTH;

		public SpinButton(int aDirection) {
			super();
			setInheritsPopupMenu(true);
			direction = aDirection;
		}

		public Dimension getPreferredSize() {
			Dimension size = super.getPreferredSize();
			size.width = Math.max(size.width, minSize.width);
			size.height = Math.max(size.height, minSize.height);
			return size;
		}

		public void paint(Graphics g) {
			Color colors[] = null;
			ButtonModel model = getModel();
			if (isEnabled()) {
				if (model.isPressed() && model.isArmed())
					colors = AbstractLookAndFeel.getTheme().getPressedColors();
				else {
					if (model.isRollover())
						colors = AbstractLookAndFeel.getTheme().getRolloverColors();
					else if (JTattooUtilities.isFrameActive(this))
						colors = AbstractLookAndFeel.getTheme().getButtonColors();
					else
						colors = AbstractLookAndFeel.getTheme().getInActiveColors();
				}
			} else
				colors = AbstractLookAndFeel.getTheme().getDisabledColors();
			JTattooUtilities.fillHorGradient(g, colors, 0, 0, getWidth(), getHeight());
			paintBorder(g);
			g.setColor(getForeground());
			int w = 4;
			int h = 3;
			int x = (getWidth() - w) / 2;
			int y = (getHeight() - h) / 2;
			if (direction == SwingConstants.NORTH) {
				for (int i = 0; i < h; i++) {
					g.drawLine(x + (h - i) - 1, y + i, x + w - (h - i) + 1, y + i);
				}
			} else {
				for (int i = 0; i < h; i++) {
					g.drawLine(x + i, y + i, x + w - i, y + i);
				}
			}
		}

	}

	//------------------------------------------------------------------------------
	private static class MyLayoutManager implements LayoutManager {
		//
		// LayoutManager
		//
		private Component nextButton = null;
		private Component previousButton = null;
		private Component editor = null;

		public void addLayoutComponent(String name, Component c) {
			if ("Next".equals(name)) {
				nextButton = c;
			} else if ("Previous".equals(name)) {
				previousButton = c;
			} else if ("Editor".equals(name)) {
				editor = c;
			}
		}

		public void removeLayoutComponent(Component c) {
			if (c == nextButton) {
				c = null;
			} else if (c == previousButton) {
				previousButton = null;
			} else if (c == editor) {
				editor = null;
			}
		}

		private Dimension preferredSize(Component c) {
			return (c == null) ? zeroSize : c.getPreferredSize();
		}

		public Dimension preferredLayoutSize(Container parent) {
			Dimension nextD = preferredSize(nextButton);
			Dimension previousD = preferredSize(previousButton);
			Dimension editorD = preferredSize(editor);

			// Force the editors height to be a multiple of 2
			editorD.height = ((editorD.height + 1) / 2) * 2;

			Dimension size = new Dimension(editorD.width, editorD.height);
			size.width += Math.max(nextD.width, previousD.width);
			Insets insets = parent.getInsets();
			size.width += insets.left + insets.right;
			size.height += insets.top + insets.bottom + 4;
			return size;
		}

		public Dimension minimumLayoutSize(Container parent) {
			return preferredLayoutSize(parent);
		}

		private void setBounds(Component c, int x, int y, int width, int height) {
			if (c != null) {
				c.setBounds(x, y, width, height);
			}
		}

		public void layoutContainer(Container parent) {
			int width = parent.getWidth();
			int height = parent.getHeight();

			Insets insets = parent.getInsets();
			Dimension nextD = preferredSize(nextButton);
			Dimension previousD = preferredSize(previousButton);
			int buttonsWidth = Math.max(nextD.width, previousD.width);
			int editorHeight = height - (insets.top + insets.bottom);

			// The arrowButtonInsets value is used instead of the JSpinner's
			// insets if not null. Defining this to be (0, 0, 0, 0) causes the
			// buttons to be aligned with the outer edge of the spinner's
			// border, and leaving it as "null" places the buttons completely
			// inside the spinner's border.
			Insets buttonInsets = UIManager.getInsets("Spinner.arrowButtonInsets");
			if (buttonInsets == null) {
				buttonInsets = insets;
			}

			// Deal with the spinner's componentOrientation property.
			int editorX, editorWidth, buttonsX;
			if (parent.getComponentOrientation().isLeftToRight()) {
				editorX = insets.left;
				editorWidth = width - insets.left - buttonsWidth - buttonInsets.right;
				buttonsX = width - buttonsWidth - buttonInsets.right;
			} else {
				buttonsX = buttonInsets.left;
				editorX = buttonsX + buttonsWidth;
				editorWidth = width - buttonInsets.left - buttonsWidth - insets.right;
			}

			int nextY = buttonInsets.top;
			int nextHeight = (height / 2) + (height % 2) - nextY;
			int previousY = buttonInsets.top + nextHeight;
			int previousHeight = height - previousY - buttonInsets.bottom;

			setBounds(editor, editorX, insets.top, editorWidth, editorHeight);
			setBounds(nextButton, buttonsX, nextY, buttonsWidth, nextHeight);
			setBounds(previousButton, buttonsX, previousY, buttonsWidth, previousHeight);
		}

	}

}
